import Layout from '@/layout'
import SkeletonPage from '@/views/_common/SkeletonPage'
import Page500 from '@/views/_app/500'
import { isEmpty } from '@/utils'
import { isExternal, isString } from '@/utils/validate'

//json字符串转路由配置
export function str2routeConfig(str) {
  const parseOpt = (key, value) => {
    if (key === 'dynamicTitle' && isString(value)) {
      return isEmpty(value) ? undefined : eval(`(${value})`)
    }
    return value
  }
  return JSON.parse(str, parseOpt)
}

//根据json数组生成路由
export function generateRoutes(jsonTree) {
  //将深度为2的节点合并为由其最末子节点组成的数组
  function mergeChildren(routes) {
    function fun(child, parent = { path: '' }, list = []) {
      const { children } = child
      if (!isEmpty(parent.path)) {
        child.path = `${parent.path}/${child.path}`
      }
      if (!children || !children.length) {
        list.push(child)
      } else {
        children.forEach((i) => fun(i, child, list))
      }
      return list
    }

    routes.forEach((root) => {
      const { children } = root
      if (children && children.length) {
        const list = []
        children.forEach((route) => {
          list.push(...fun(route))
        })
        root.children = list
      }
    })
  }

  //数据转换，同时剔除不合法的路由
  function transform(routes, depth = 1) {
    if (!Array.isArray(routes)) return

    for (let i = routes.length - 1; i >= 0; i--) {
      const { path, redirect, component, children, meta = {} } = routes[i]

      //剔除外链节点以及无component且非iframe的节点
      if (isExternal(path) || (!component && !meta.iframe)) {
        routes.splice(i, 1)
        continue
      }

      //清理一些空属性，避免vue-router出错
      ;['name', 'component', 'meta'].forEach((key) => {
        if (key in routes[i] && isEmpty(routes[i][key])) {
          delete routes[i][key]
        }
      })

      //设置路由组件，当component是vue组件声明对象时，不会进行懒加载
      if (component) {
        if (isString(component) && component.toUpperCase() === 'LAYOUT') {
          routes[i].component = Layout
        } else if (isString(component) || typeof component === 'function') {
          routes[i].component = lazyLoadView(component)
        }
      }

      //先尝试递归
      transform(children, depth + 1)

      //叶子节点统一设置props = true，省的动态路由还要手动加
      if (!children || children.length <= 0) {
        routes[i].props = true
      }
      //仅在根节点未设置redirect时，将其第一个子节点的路径设为redirect
      else if (depth === 1 && isEmpty(redirect)) {
        const rootPath = path.endsWith('/') ? path : `${path}/`
        routes[i].redirect = `${rootPath}${children[0].path}`
      }
    }
  }

  //路由页面懒加载，入参可以是字符串、返回值为Promise的函数
  function lazyLoadView(component) {
    if (isString(component)) {
      if (component.endsWith('/')) {
        component += 'index'
      } else if (component.endsWith('Page')) {
        component = component.replace('Page', '')
      } else {
        component = component.replace('Page.vue', '')
      }
    }

    //这里注意一点，如果设置了超时时间，那么超时后只能刷新整个页面才能重新加载该组件
    const AsyncHandler = () => ({
      component:
        typeof component === 'function'
          ? component()
          : import(`@/views/${component}Page.vue`),
      loading: SkeletonPage,
      error: Page500,
      delay: 200,
      timeout: 10000
    })

    return () =>
      Promise.resolve({
        abstract: true,
        functional: true,
        render(h, { data, children }) {
          return h(AsyncHandler, data, children)
        }
      })
  }

  mergeChildren(jsonTree)
  transform(jsonTree)

  return jsonTree
}

//子路由继承父路由meta上的{noAuth, noCache}，优先使用子路由的值
export function metaExtend(routes, parentMeta) {
  const keys = ['noAuth', 'noCache', 'roles']
  routes.forEach((route) => {
    if (parentMeta) {
      Object.keys(parentMeta).forEach((key) => {
        const value = parentMeta[key]
        if (keys.includes(key) && !isEmpty(value)) {
          if (!route.meta) {
            route.meta = { [key]: value }
          } else if (isEmpty(route.meta[key])) {
            route.meta[key] = value
          }
        }
      })
    }
    route.children && metaExtend(route.children, route.meta)
  })
}

//将require.context导出的对象处理为数组
export function context2array(context) {
  return context.keys().reduce((modules, modulePath) => {
    const value = context(modulePath).default
    Array.isArray(value) ? modules.push(...value) : modules.push(value)
    return modules
  }, [])
}
